package utils

import (
	cryptorand "crypto/rand"
	"encoding/binary"
	"fmt"
	"math"
	"math/rand"

	"time"
)

const (
	maxNum int = 900000
	minNum int = 100000
)

// 百分比随机数值
const (
	percentageStart int32 = 1   //百分比随机数值开始
	percentageEnd   int32 = 100 //百分比随机数值结束
)

const (
	Tenfold      int32 = 10   // 十倍
	Hundredfold  int32 = 100  // 百倍
	Thousandfold int32 = 1000 // 千倍
)

// generateSecureSeed 生成高熵种子
func generateSecureSeed() int64 {
	var seedBuf [16]byte
	if _, err := cryptorand.Read(seedBuf[:]); err == nil {
		// 将16字节拆分为两个uint64进行异或
		high := binary.BigEndian.Uint64(seedBuf[:8])
		low := binary.BigEndian.Uint64(seedBuf[8:])
		return int64(high ^ low) // 通过异或合并两部分熵
	}

	// 原有回退方案保持不变
	return time.Now().UnixNano() ^ int64(math.Float64bits(float64(time.Now().Nanosecond())))
}

// RandomRange 区间随机 [min,max]
func RandomRange(min int, max int) int {
	if min == 0 && max == 0 {
		return 0
	}
	if min > max {
		panic(fmt.Errorf("min[%d]应该不超过max[%d]", min, max))
	}
	diff := max - min
	if diff == 0 {
		return min
	}
	randomNum := int64(rand.Intn(maxNum) + minNum)
	rand.Seed(time.Now().UnixNano() + randomNum)
	return rand.Intn(diff+1) + int(min)
}

// RandomRangeI64 区间随机 [min,max]
func RandomRangeI64(min, max int64) int64 {
	if min == 0 && max == 0 {
		return 0
	}
	if min > max {
		panic(fmt.Errorf("min[%d]应该不超过max[%d]", min, max))
	}
	diff := max - min
	if diff == 0 {
		return min
	}
	randomNum := int64(rand.Intn(maxNum) + minNum)
	rand.Seed(time.Now().UnixNano() + randomNum)
	return rand.Int63n(diff+1) + min
}

// CryptRandomRangeI64 区间随机 [min,max]
func CryptRandomRangeI64(min, max int64) int64 {
	if min == 0 && max == 0 {
		return 0
	}
	if min > max {
		panic(fmt.Errorf("min[%d]应该不超过max[%d]", min, max))
	}
	diff := max - min
	if diff == 0 {
		return min
	}

	// 生成密码学安全的随机种子
	seed := generateSecureSeed()
	// 创建局部随机源
	localRand := rand.New(rand.NewSource(seed))
	return localRand.Int63n(diff+1) + min
}

// RandomIndex 区间下标
func RandomIndex(scop int) int {
	randomNum := int64(rand.Intn(maxNum) + minNum)
	rand.Seed(time.Now().UnixNano() + randomNum)
	return rand.Intn(scop)
}

// RandomWeights 随机从权重
func RandomWeights(weights []int64) (index int) {
	totalWeight := int64(0)
	for _, weight := range weights {
		if weight < 0 {
			panic("weight should be no less than 0")
		}
		totalWeight += weight
	}
	if totalWeight == 0 {
		return -1
	}
	randomNums := int64(rand.Intn(maxNum) + minNum)
	rand.Seed(time.Now().UnixNano() + randomNums)
	randomNum := rand.Int63n(totalWeight) + 1
	Printf("weights:%+v, rand:%d, totalWeight:%+d", weights, randomNum, totalWeight)
	for i, weight := range weights {
		if randomNum > weight {
			randomNum -= weight
			continue
		}
		index = i
		break
	}
	return
}

// RandomBalanceWeights 随机平衡权重
func RandomBalanceWeights(sourceMap, changeMap map[int32]int64, balance int32) int32 {
	var changeTotal int64
	var rs = make(map[int32]int64)
	for k, v := range sourceMap {
		rs[k] = v
	}
	for k, v := range changeMap {
		changeTotal += v
		rs[k] += v
		rs[balance] -= v
	}
	if rs[balance] < 0 {
		rs[balance] = 0
	}
	var (
		nums    []int32
		weights []int64
	)
	for k, v := range rs {
		nums = append(nums, k)
		weights = append(weights, v)
	}
	Printf("RandomBalanceWeights|nums:%+v, weights:%+v", nums, weights)
	return nums[RandomWeights(weights)]
}

// RandomMap 随机map
func RandomMap(source map[int32]int64) int32 {
	var (
		nums    []int32
		weights []int64
	)
	for k, v := range source {
		nums = append(nums, k)
		weights = append(weights, v)
	}
	Printf("RandomMap|keys:%+v, values:%+v", nums, weights)
	return nums[RandomWeights(weights)]
}

// GetRandomIdByWeight 根据权重随机出对应的id
func GetRandomIdByWeight(idSource []int32, weightSource []int64, num int) []int32 {
	var (
		ids     = make([]int32, len(idSource))
		weights = make([]int64, len(weightSource))
	)
	copy(ids, idSource)
	copy(weights, weightSource)

	if len(ids) != len(weights) || len(ids) < num {
		return nil
	}
	randomNum := int64(rand.Intn(maxNum) + minNum)
	rand.Seed(time.Now().UnixNano() + randomNum)
	n := len(ids)

	cumWeights := make([]int64, n)
	cumWeights[0] = weights[0]
	for i := 1; i < n; i++ {
		cumWeights[i] = cumWeights[i-1] + weights[i]
	}

	result := make([]int32, num)
	for i := range result {
		pick := int64(rand.Intn(int(cumWeights[n-1])) + 1)
		selected := 0
		for cumWeights[selected] < pick {
			selected++
		}

		result[i] = ids[selected]
		copy(ids[selected:], ids[selected+1:])
		copy(weights[selected:], weights[selected+1:])
		n--
		if selected < n {
			cumWeights[selected] = cumWeights[selected+1]
		}
		for j := selected + 1; j < n; j++ {
			cumWeights[j] = cumWeights[j] + weights[j]
		}
	}
	return result
}

// RandomNotRepeated
func RandomNotRepeated(scop, count int) ([]int, error) {
	if scop+1 < count {
		return nil, fmt.Errorf("range of %d is too small to generate %d unique numbers", scop, count)
	}

	// Initialize random seed
	randomNum := int64(rand.Intn(maxNum) + minNum)
	rand.Seed(time.Now().UnixNano() + randomNum)

	// Generate a slice with numbers from 0 to scop
	numbers := make([]int, scop)
	for i := range numbers {
		numbers[i] = i
	}

	// Shuffle the slice
	rand.Shuffle(len(numbers), func(i, j int) {
		numbers[i], numbers[j] = numbers[j], numbers[i]
	})

	// Select the first 'count' numbers from the shuffled slice

	return numbers[:count], nil
}

// RandomNotRepeatedI32Arr
func RandomNotRepeatedI32Arr(arr []int32, count int) ([]int32, error) {
	if len(arr) < count {
		return nil, fmt.Errorf("range of %d is too small to generate %d unique numbers", len(arr), count)
	}
	// Shuffle the slice
	rand.Shuffle(len(arr), func(i, j int) {
		arr[i], arr[j] = arr[j], arr[i]
	})
	return arr[:count], nil
}

func RandomRangeF64(min, max float64) float64 {
	if min == 0 && max == 0 {
		return 0
	}
	if min > max {
		panic(fmt.Errorf("min[%v]应该不超过max[%v]", min, max))
	}
	diff := max - min
	if diff == 0 {
		return min
	}
	randomNum := int64(rand.Intn(maxNum) + minNum)
	rand.Seed(time.Now().UnixNano() + randomNum)
	return float64(min) + rand.Float64()*(max-float64(min))
}

func RandomRangeI32(min int32, max int32) int32 {
	if min == 0 && max == 0 {
		return 0
	}
	if min > max {
		panic(fmt.Errorf("min[%d]应该不超过max[%d]", min, max))
	}
	diff := max - min
	if diff == 0 {
		return min
	}
	randomNum := int64(rand.Intn(maxNum) + minNum)
	rand.Seed(time.Now().UnixNano() + randomNum)
	return rand.Int31n(diff+1) + min
}

// 切片随机
func RandomRangeSlice(SliceInfo []int32) int32 {
	if len(SliceInfo) == 0 {
		return 0
	}
	randomNum := int64(rand.Intn(maxNum) + minNum)
	rand.Seed(time.Now().UnixNano() + randomNum)
	//打乱切片顺序
	rand.Shuffle(len(SliceInfo), func(i, j int) {
		SliceInfo[i], SliceInfo[j] = SliceInfo[j], SliceInfo[i]
	})
	return SliceInfo[0]
}

// 百分比概率是否成功[]
func IsRandomOk(val int32, enlargement int32) bool {
	if val == 0 {
		return false
	}
	// 值扩大
	val = int32(NewDecimal(float64(val)).Mul(float64(enlargement)).Float64())
	// 比例扩大
	maxEnlargement := int32(NewDecimal(float64(percentageEnd)).Mul(float64(enlargement)).Float64())
	minEnlargement := int32(NewDecimal(float64(percentageStart)).Mul(float64(enlargement)).Float64())
	randomNum := int64(rand.Intn(maxNum) + minNum)
	rand.Seed(time.Now().UnixNano() + randomNum)
	diff := maxEnlargement - minEnlargement
	if diff == 0 {
		return false
	}
	randVal := rand.Int31n(diff) + minEnlargement
	Printf("IsRandomOk|val:%d, randVal:%d", val, randVal)
	return randVal <= val
}

// RandomMapI64 随机map-int64
func RandomMapI64(source map[int64]int64) int64 {
	var (
		nums    []int64
		weights []int64
	)
	for k, v := range source {
		nums = append(nums, k)
		weights = append(weights, v)
	}
	Printf("RandomMapI64|keys:%+v, values:%+v", nums, weights)
	return nums[RandomWeights(weights)]
}
